#include <cstdio>
#include <string>
#include <cstring>

#pragma warning(disable : 4996)
#pragma pack(1)
#define GEOM_GET_CYLS(_g)  (((_g) >> 16) & 0xffff) 
#define GEOM_GET_HEADS(_g) (((_g) >> 8)  & 0xff) 
#define GEOM_GET_SPT(_g)   ((_g) & 0xff) 

using namespace std;
using u32 = unsigned;
using u64 = unsigned long long;

struct VhdFooter {
	char cookie[8];
	u32 features;
	u32 ff_version;
	u64 data_offset;
	u32 timestamp;
	char crtr_app[4];
	u32 crtr_ver;
	u32 crtr_os;
	u64 orig_size;
	u64 curr_size;
	u32 geometry;
	u32 type;
	u32 checksum;
	char uuid[16];
	char saved;
	char hidden;
	char reserved[426];
};

struct File {
	FILE* fp;

	File(const string& filename, const string& mode) {
		fp = fopen(filename.c_str(), mode.c_str());
		if (!fp) {
			throw "无法打开文件：" + filename + " --- " + strerror(errno);
		}
	}

	~File() {
		fclose(fp);
	}
};

bool strCmp(const char* str1, const char* str2, size_t size) {
	size--;
	while (size) {
		if (str1[size] != str2[size]) {
			return false;
		}
		size--;
	}
	return true;
}

unsigned ntoh(unsigned num) {
	unsigned res = 0;
	res += num & 0xff;
	res <<= 8;
	res += (num & 0xff00) >> 8;
	res <<= 8;
	res += (num & 0xff0000) >> 16;
	res <<= 8;
	res += (num & 0xff000000) >> 24;
	return res;
}

unsigned long long ntoh(unsigned long long num) {
	unsigned long long res = 0;
	res += num & 0xff;
	res <<= 8;
	res += (num & 0xff00) >> 8;
	res <<= 8;
	res += (num & 0xff0000) >> 16;
	res <<= 8;
	res += (num & 0xff000000) >> 24;
	res <<= 8;
	res += (num & 0xff00000000) >> 32;
	res <<= 8;
	res += (num & 0xff0000000000) >> 40;
	res <<= 8;
	res += (num & 0xff000000000000) >> 48;
	res <<= 8;
	res += (num & 0xff00000000000000) >> 56;
	return res;
}

unsigned str2num_sub(const string& strnum, int scale) {
	if (strnum.size() == 0) {
		throw 0;
	}
	unsigned res = 0;
	for (auto ch : strnum) {
		if (scale == 16) {
			if (ch >= 'A' && ch <= 'F') {
				ch =  ch - 'A' + 10;
			}
			else if (ch >= 'a' && ch <= 'f') {
				ch = ch - 'a' + 10;
			}
			else if (ch >= '0' && ch <= '9') {
				ch -= '0';
			}
			else {
				throw 0;
			}
		}
		else {
			if (ch >= '0' && ch <= '9') {
				ch -= '0';
			}
			else {
				throw 0;
			}
		}
		if (ch >= scale) {
			throw 0;
		}
		res = res * scale + ch;
	}
	return res;
}

unsigned str2num(const string& strnum) {
	try {
		if (strnum[0] == '0') {
			if (strnum.size() == 1) {
				return 0;
			}
			if (strnum[1] == 'b' || strnum[1] == 'B') {
				return str2num_sub(strnum.substr(2, strnum.size() - 2), 2);
			}
			else if (strnum[1] == 'o' || strnum[1] == 'O') {
				return str2num_sub(strnum.substr(2, strnum.size() - 2), 8);
			}
			else if (strnum[1] == 'd' || strnum[1] == 'D') {
				return str2num_sub(strnum.substr(2, strnum.size() - 2), 10);
			}
			else if (strnum[1] == 'x' || strnum[1] == 'X') {
				return str2num_sub(strnum.substr(2, strnum.size() - 2), 16);
			}
			else if (strnum[1] >= '0' && strnum[1] <= '9') {
				return str2num_sub(strnum.substr(1, strnum.size() - 1), 8);
			}
			else {
				throw 0;
			}
		}
		else if (strnum[0] > '0' && strnum[0] <= '9') {
			return str2num_sub(strnum, 10);
		}
		else {
			throw 0;
		}
	}
	catch (int e) {
		throw strnum + "不是一个有效的正整数（2进制，8进制，10进制，16进制）";
	}
}

int main(int argc, char** argv) {
	if (argc == 1) {
		puts("fixvhdwriter vhd文件");
		puts("fixvhdwriter vhd文件 待写入的文件 lba号");
	}
	else {
		try {
			File vhdFile(argv[1], "rb+");
			VhdFooter vhdFooter;
			fseek(vhdFile.fp, -(long)sizeof(vhdFooter), SEEK_END);
			fread(&vhdFooter, sizeof(vhdFooter), 1, vhdFile.fp);
			vhdFooter.features = ntoh(vhdFooter.features);
			vhdFooter.ff_version = ntoh(vhdFooter.ff_version);
			vhdFooter.data_offset = ntoh(vhdFooter.data_offset);
			vhdFooter.timestamp = ntoh(vhdFooter.timestamp);
			vhdFooter.crtr_ver = ntoh(vhdFooter.crtr_ver);
			vhdFooter.crtr_os = ntoh(vhdFooter.crtr_os);
			vhdFooter.orig_size = ntoh(vhdFooter.orig_size);
			vhdFooter.curr_size = ntoh(vhdFooter.curr_size);
			vhdFooter.geometry = ntoh(vhdFooter.geometry);
			vhdFooter.type = ntoh(vhdFooter.type);
			vhdFooter.checksum = ntoh(vhdFooter.checksum);
			if (!strCmp(vhdFooter.cookie, "conectix", 8)) {
				throw string(argv[1]) + "不是有效的vhd文件";
			}
			if (vhdFooter.type != 2) {
				throw string(argv[1]) + "不是固定大小的vhd文件";
			}
			if (argc == 2) {
				printf("磁盘容量：%llu\n", vhdFooter.curr_size);
				printf("柱面：%u\n", GEOM_GET_CYLS(vhdFooter.geometry));
				printf("磁头：%u\n", GEOM_GET_HEADS(vhdFooter.geometry));
				printf("扇区：%u\n", GEOM_GET_SPT(vhdFooter.geometry));
			}
			else if (argc == 4) {
				unsigned lba = str2num(argv[3]);
				if (lba >= vhdFooter.curr_size / 512) {
					throw string("lba超出磁盘最大容量");
				}
				File binFile(argv[2], "rb");
				fseek(vhdFile.fp, lba * 512, SEEK_SET);
				int ch;
				while ((ch = fgetc(binFile.fp)) != EOF) {
					fputc(ch, vhdFile.fp);
				}
			}
			else {
				throw string("命令行参数错误");
			}
		}
		catch (string& e) {
			fprintf(stderr, "%s\n", e.c_str());
			return 1;
		}
	}
	return 0;
}